/**
 * Copyright (C) 2012  The CFuture Android SDK Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.lurencun.android.toolkit.file;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Random;

import android.util.Log;

/**
 * 通用的文件处理类。</br>
 * 目前设计用于Android平台中，如果要改到通用Java平台，请把debug,warn,error,三个方法里的Log改为其它Log类。
 * 
 * @author cfuture.chenyoca [桥下一粒砂] (chenyoca@163.com)
 * @date 2012-2-17
 */
public class FileUtil {
	private final static String DEBUG_TAG = "CHEN_DEB";
	private final static String ERROR_TAG = "CHEN_ERR";
	private final static String WARN_TAG = "CHEN_WAR";
	
	private static void debug(String format, Object... args) {
		Log.d(DEBUG_TAG,String.format(format, args));
	}

	private static void warn(String format, Object... args) {
		Log.w(WARN_TAG,String.format(format, args));
	}

	private static void error(String format, Object... args) {
		Log.e(ERROR_TAG,String.format(format, args));
	}

	/**
	 * 把源文件复制到目标文件中。复制成功返回Ture，否则返回False。
	 * @param source 
	 * 		源文件
	 * @param dest 
	 * 		目标文件
	 * @throws IOException
	 * 		如果源文件不存在或者目标文件不可写入，抛出IO异常。
	 */
	public static void copyFile(File source, File dest) throws IOException {
		debug("Copy from %1$s to %2$s", source.getAbsoluteFile(),
				dest.getAbsoluteFile());
		
		FileInputStream fi = null;
		FileOutputStream fo = null;
		try{
			fi = new FileInputStream(source);
			fo = new FileOutputStream(dest);
		}catch(FileNotFoundException ex){
			error("Source File not exist !");
		}
		FileChannel fic = fi.getChannel();
		MappedByteBuffer mbuf = fic.map(FileChannel.MapMode.READ_ONLY, 0,
				source.length());
		fic.close();
		fi.close();
		fi = null;
		// ensure the destination directory exists
		if (!dest.exists()) {
			String destPath = dest.getPath();
			debug("Destination path: %1$s", destPath);
			String destDir = destPath.substring(0,
					destPath.lastIndexOf(File.separatorChar));
			debug("Destination dir: %1$s", destDir);
			File dir = new File(destDir);
			if (!dir.exists()) {
				if (dir.mkdirs()) {
					debug("Directory created");
				} else {
					warn("Directory not created");
				}
			}
			dir = null;
		}

		FileChannel foc = fo.getChannel();
		foc.write(mbuf);
		foc.close();
		fo.close();
		fo = null;

		mbuf.clear();
		mbuf = null;
	}

	/**
	 * 复制文件
	 * @param source 源文件路径
	 * @param dest 目标文件路径
	 * @throws IOException
	 */
	public static void copyFile(String source, String dest) throws IOException {
		copyFile(new File(source), new File(dest));
	}

	/**
	 * 移动文件
	 * @param source 
	 * 				源文件路径 
	 * @param dest 
	 * 				目标文件路径 
	 * @throws IOException
	 */
	public static void moveFile(String source, String dest) throws IOException {
		copyFile(source, dest);
		File src = new File(source);
		if (src.exists() && src.canRead()) {
			if (src.delete()) {
				debug("Source file was deleted");
			} else {
				debug("Source file was not deleted, the file will be deleted on exit");
				src.deleteOnExit();
			}
		} else {
			warn("Source file could not be accessed for removal");
		}
		src = null;
	}

	/**
	 * 删除文件夹及其下内容。如果文件夹被系统锁定或者文件夹不能被清空，将返回false。
	 * @param directory
	 *            要被删除的文件夹
	 * @throws IOException
	 *             如果文件夹不能被删除，则抛出异常。
	 * @return 文件夹删除成功则返回true，文件夹不存在则返回false。
	 */
	public static boolean deleteDirectory(String directory) throws IOException {
		return deleteDirectory(directory, false);
	}

	/**
	 * 删除文件夹及其下内容。如果文件夹被系统锁定或者文件夹不能被清空，将返回false。
	 * 
	 * @param directory
	 *            要被删除的文件夹
	 * @param useOSNativeDelete
	 *            标识是否使用系统命令进行删除操作。
	 * @throws IOException
	 *             如果文件夹不能被删除，则抛出异常。
	 * @return 文件夹删除成功则返回true，文件夹不存在则返回false。
	 */
	public static boolean deleteDirectory(String directory,
			boolean useOSNativeDelete) throws IOException {
		boolean result = false;
		if (!useOSNativeDelete) {
			File dir = new File(directory);
			// first all files have to be cleared out
			for (File file : dir.listFiles()) {
				if (file.delete()) {
					debug("%1$s was deleted", file.getName());
				} else {
					debug("%1$s was not deleted", file.getName());
					file.deleteOnExit();
				}
				file = null;
			}
			// not you may remove the dir
			if (dir.delete()) {
				debug("Directory was deleted");
				result = true;
			} else {
				debug("Directory was not deleted, it may be deleted on exit");
				dir.deleteOnExit();
			}
			dir = null;
		} else {
			Process p = null;
			Thread std = null;
			try {
				Runtime runTime = Runtime.getRuntime();
				debug("Execute runtime");
				// determine file system type
				if (File.separatorChar == '\\') {
					// we are windows
					p = runTime.exec("CMD /D /C \"RMDIR /Q /S "
							+ directory.replace('/', '\\') + "\"");
				} else {
					// we are unix variant
					p = runTime.exec("rm -rf "
							+ directory.replace('\\', File.separatorChar));
				}
				// observe std out
				std = stdOut(p);
				// wait for the observer threads to finish
				while (std.isAlive()) {
					try {
						Thread.sleep(250);
					} catch (Exception e) {
					}
				}
				debug("Process threads wait exited");
				result = true;
			} catch (Exception e) {
				error("Error running delete script", e);
			} finally {
				if (null != p) {
					debug("Destroying process");
					p.destroy();
					p = null;
				}
				std = null;
			}
		}
		return result;
	}

	/**
	 * 使用本地系统命令重命名一个文件。
	 * @param from
	 *            原文件名
	 * @param to
	 *            新文件名
	 */
	public static void rename(String from, String to) {
		Process p = null;
		Thread std = null;
		try {
			Runtime runTime = Runtime.getRuntime();
			debug("Execute runtime");
			// determine file system type
			if (File.separatorChar == '\\') {
				// we are windows
				p = runTime.exec("CMD /D /C \"REN " + from + ' ' + to + "\"");
			} else {
				// we are unix variant
				p = runTime.exec("mv -f " + from + ' ' + to);
			}
			// observe std out
			std = stdOut(p);
			// wait for the observer threads to finish
			while (std.isAlive()) {
				try {
					Thread.sleep(250);
				} catch (Exception e) {
				}
			}
			debug("Process threads wait exited");
		} catch (Exception e) {
			error("Error running delete script", e);
		} finally {
			if (null != p) {
				debug("Destroying process");
				p.destroy();
				p = null;
				std = null;
			}
		}
	}

	/**
	 * Special method for capture of StdOut.
	 * 
	 * @return
	 */
	private final static Thread stdOut(final Process p) {
		final byte[] empty = new byte[128];
		for (int b = 0; b < empty.length; b++) {
			empty[b] = (byte) 0;
		}
		Thread std = new Thread() {
			public void run() {
				StringBuilder sb = new StringBuilder(1024);
				byte[] buf = new byte[128];
				BufferedInputStream bis = new BufferedInputStream(
						p.getInputStream());
				debug("Process output:");
				try {
					while (bis.read(buf) != -1) {
						sb.append(new String(buf).trim());
						// clear buffer
						System.arraycopy(empty, 0, buf, 0, buf.length);
					}
					debug(sb.toString());
					bis.close();
				} catch (Exception e) {
					error("%1$s", e);
				}
			}
		};
		std.setDaemon(true);
		std.start();
		return std;
	}

	/**
	 * 创建一个文件夹。
	 * 
	 * @param directory
	 *            需要被创建的文件夹
	 * @return 创建成功则返回true，否则返回false。
	 * @throws IOException
	 *             如果文件已经存在，或者不能被创建，则抛出异常。
	 */
	public static boolean makeDirectory(String directory) throws IOException {
		return makeDirectory(directory, false);
	}

	/**
	 * 创建一个文件夹. 如果<i>createParents</i> 被标记为true，则父级文件夹不存在将会被自动创建。
	 * 
	 * @param directory
	 *            需要被创建的文件夹
	 * @param createParents
	 *            是否创建父级文件夹标识
	 * @return 如果文件夹创建成功，返回true。如果文件夹已经存在，返回false。
	 * @throws IOException
	 *             如果文件夹不能被创建，则抛出异常
	 * 
	 */
	public static boolean makeDirectory(String directory, boolean createParents)
			throws IOException {
		boolean created = false;
		File dir = new File(directory);
		if (createParents) {
			created = dir.mkdirs();
			if (created) {
				debug("Directory created: %1$s", dir.getAbsolutePath());
			} else {
				debug("Directory was not created: %1$s", dir.getAbsolutePath());
			}
		} else {
			created = dir.mkdir();
			if (created) {
				debug("Directory created: %1$s", dir.getAbsolutePath());
			} else {
				debug("Directory was not created: %1$s", dir.getAbsolutePath());
			}
		}
		dir = null;
		return created;
	}

	/**
	 * 生成一个文件名。类似 282818_00023 。这个名字由于当前秒数加随机数组成。
	 * @return 生成的文件名
	 */
	public static String generateCustomName() {
		Random random = new Random();
		StringBuilder sb = new StringBuilder();
		sb.append(System.nanoTime());
		sb.append('_');
		int i = random.nextInt(99999);
		if (i < 10) {
			sb.append("0000");
		} else if (i < 100) {
			sb.append("000");
		} else if (i < 1000) {
			sb.append("00");
		} else if (i < 10000) {
			sb.append("0");
		}
		sb.append(i);
		return sb.toString();
	}

}